
#include "../udp.h"
#include "../uv_common.h"
#include "../uv_thread.h"
#include <errno.h>
#include <net/if.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/ioctl.h>

static int g_epollfd = -1;

static void* UWorkerThread(void* parm)
{
    int epollfd = *(int*)parm;
    static const int max_safe_timeout = 1789569;
    struct epoll_event events[1024];

    while (1) {
        int ret = epoll_wait(epollfd, events, ARRAY_SIZE(events), max_safe_timeout);

        if (ret > 0) {
            for (int i = 0; i < ret; i++) {

                printf("%x %p\n", events[i].events, events[i].data.ptr);
                if (EPOLLIN & events[i].events) {
                    udp_t* udphandler = (udp_t*)events[i].data.ptr;

                    uv_buf_t buf = { RECVBUFLEN, (char*)malloc(RECVBUFLEN) };

                    struct sockaddr addr;
                    socklen_t addrlen = sizeof(addr);
                    ssize_t nread = recvfrom(udphandler->socket, buf.base, RECVBUFLEN, 0, &addr, &addrlen);
                    printf("%s:nread = %zd\n", __func__, nread);
                    if (udphandler->recvReq->cb) {
                        udphandler->recvReq->cb(udphandler->recvReq, nread, &buf, &addr, ret);
                    } else {
                        free(buf.base);
                    }
                } else {
                    printf("%s : unhandled\n", __func__);
                }
            }
        } else {
            printf("%s:%d %d\n", __func__, ret, errno);
        }
    }

    return NULL;
}

int udp_init()
{
    if (g_epollfd == -1) {
        if ((g_epollfd = epoll_create(0x7fffffff)) == -1) {
            return errno;
        }
        uv_thread_t tid;
        uv_thread_create(&tid, (uv_thread_cb)UWorkerThread, &g_epollfd);
    }
    return 0;
}

int udp_createhandler(udp_t* udphandle, struct sockaddr* addr, size_t addrlen)
{
    int ret = 0;
    //udphandle = (udp_t*)calloc(1, sizeof(udp_t));
    udphandle->recvReq = NULL;
    if ((udphandle->socket = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
        return errno;
    }

    //set reuse;
    int opt = SO_REUSEADDR; //TBD
    setsockopt(udphandle->socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    if (fcntl(udphandle->socket, F_SETFL, fcntl(udphandle->socket, F_GETFD, 0) | O_NONBLOCK) == -1) {
        printf("%s set noblock failed %d\n", __func__, errno);
        return errno;
    }

    if (addr == NULL || addrlen <= 0) {
        uv_ip4_addr("0.0.0.0", 0, (struct sockaddr_in*)&udphandle->addr);
        addrlen = sizeof(udphandle->addr);
    } else {
        memcpy(&udphandle->addr, addr, addrlen);
    }

    if (bind(udphandle->socket, (struct sockaddr*)&udphandle->addr, addrlen) == -1) {
        ret = errno;
        printf("bind failed %d\n", ret);
        return ret;
    }

    udp_getsockname(udphandle, &udphandle->addr, (int*)&addrlen);

    return ret;
}

#define INITCOMMONREQ(req, type)      \
    if (req) {                   \
        req->optype = type;      \
        req->handle = udphandle; \
        req->cb = cb;            \
    }

void udp_recv_start(udp_t* udphandle, uv_recv_cb cb)
{
    struct epoll_event ev;
    ev.events = EPOLLIN;
    if(udphandle->recvReq == NULL){
        udphandle->recvReq = (RecvReq_t*)malloc(sizeof(RecvReq_t));
        INITCOMMONREQ(udphandle->recvReq, RECV)
    }

    ev.data.ptr = udphandle;
    printf("%s %p\n", __func__, ev.data.ptr);

    if (epoll_ctl(g_epollfd, EPOLL_CTL_ADD, udphandle->socket, &ev) < 0) {
        fprintf(stderr, "epoll set insertion error:%d\n", errno);
        return;
    } else {
        udphandle->recvReq->started = 1;
        printf("%s:add socket to epoll success\n", __func__);
    }
}

void udp_recv_stop(udp_t *udphandle)
{
    if(udphandle->recvReq && udphandle->recvReq->started){
        epoll_ctl(g_epollfd, EPOLL_CTL_DEL, udphandle->socket, NULL);
        udphandle->recvReq->started = 0;
    }
}

#define MAX_UDP_SENDUNIT (1400)

void udp_send(SendReq_t* req, udp_t* udphandle, const char* buf, size_t len, const struct sockaddr* addr, uv_send_cb cb)
{
    int ret = 0;
    int addrlen = sizeof(struct sockaddr_in);
    if (addr->sa_family == PF_INET6) {
        addrlen = sizeof(struct sockaddr_in6);
    }

    size_t sendlen = 0;
    size_t leftlen = len % MAX_UDP_SENDUNIT;
    if (leftlen == 0) {
        leftlen = MAX_UDP_SENDUNIT;
    }

#if 0
    struct ifreq ifr;

    memset(&ifr, 0, sizeof(ifr));
    strncpy(ifr.ifr_name, "eth1", sizeof(ifr.ifr_name));

    if (-1 == ioctl(udphandle->socket, SIOCGIFTXQLEN, &ifr))
        printf("failed to get dev eth0 queue length\n");
    printf("Dev eth0 queue length %d\n", ifr.ifr_qlen);


    ifr.ifr_qlen = 10000;
    if (-1 == ioctl(udphandle->socket, SIOCSIFTXQLEN, &ifr))
        printf("failed to set dev eth0 queue length\n");
    if (-1 == ioctl(udphandle->socket, SIOCGIFTXQLEN, &ifr))
        printf("failed to get dev eth0 queue length\n");
    printf("Dev eth0 queue length %d\n",ifr.ifr_qlen);
#endif

#if 0
    if(-1 == connect(udphandle->socket, addr, addrlen)){
        printf("connect:%m\n");
        exit(-1);
    }
#endif

    if (len > MAX_UDP_SENDUNIT) {
        size_t loopsendlen = len - leftlen;
        do {
            ret = sendto(udphandle->socket, buf + sendlen, MAX_UDP_SENDUNIT, 0, addr, addrlen);
            //ret = send(udphandle->socket, buf + sendlen, MAX_UDP_SENDUNIT, 0);
            if (ret != MAX_UDP_SENDUNIT) {
                printf("%s:ret=%d:%d:%s\n", __func__, ret, errno, strerror(errno));
                exit(-1);
                goto RET;
            } else {
                sendlen += ret;
                //printf("send:%d\n", sendlen);
            }
        } while (sendlen < loopsendlen);
    }

    //On success, these calls return the number of bytes sent.  On error, -1 is returned, and errno is set appropriately
    ret = sendto(udphandle->socket, buf + sendlen, leftlen, 0, addr, addrlen);
    //ret = send(udphandle->socket, buf + sendlen, leftlen, 0);
    if (ret != leftlen) {
        printf("%s:ret=%d:%d:%s\n", __func__, ret, errno, strerror(errno));
        exit(-1);
    } else {
        sendlen += ret;
        //printf("ssend:+%d=%d\n", ret, sendlen);
    }
RET:
    if (cb) {
        cb(req, errno);
    }
}

void udp_shutdown(ReqCommon_t* req, udp_t* udphandle, uv_shutdown_cb cb)
{
    int ret = 0;
    //INITCOMMONREQ(SHUTDOWN)

    shutdown(udphandle->socket, SHUT_RDWR);
}

int udp_close(udp_t* udphandle)
{
    if (close(udphandle->socket)) {
        return errno;
    }
    return 0;
}

int udp_getsockname(const udp_t* handle, struct sockaddr* name, int* namelen)
{
    if (handle->socket == -1) {
        return EINVAL;
    }

    if (getsockname(handle->socket, name, namelen) != 0) {
        return errno;
    }

    return 0;
}
